package de.backtomedieval.game;

import java.util.ArrayList;

import android.util.Log;

import de.backtomedieval.R;
import de.backtomedieval.error.BuildingAlreadyExitsException;
import de.backtomedieval.error.NotEnoughWaresException;
import de.backtomedieval.main.Game;
import de.backtomedieval.buildings.*;
import de.backtomedieval.research.AbstractResearch;
import de.backtomedieval.research.Technology;
import de.backtomedieval.units.AbstractUnit;
import de.backtomedieval.units.UnitGroup;
import de.backtomedieval.units.UnitOrder;
import de.backtomedieval.units.UnitSet;

public class City {

	// private UnitGroup units = new UnitGroup();
	private UnitSet units;
	private ArrayList<AbstractBuilding> buildings = null;
	private int[] wares = { 50, 50, 100, 100, 50 };
	private int[] maxDepotSpace = { 1000, 1000, 1000, 1000, 1000 };
	private int[] updateWares = { 0, 0, 0, 0, 0 };
	private String[] waresNames = Game.getStringArray(R.array.wares);
	private String name = "";
	private Technology technology;

	private Lumberjack lumberjack = new Lumberjack();
	private Farm farm = new Farm();
	private Quarry quarry = new Quarry();
	private Timber_Yard timber_yard = new Timber_Yard();
	private Barracks barrack = new Barracks();
	private Siege_Manufacture manufacture = new Siege_Manufacture();
	private Fishing_Hut fishing_hut = new Fishing_Hut();
	private Ore_Mine ore_mine = new Ore_Mine();
	private Residential_Area residential_area = new Residential_Area();
	private Defence_Wall defence_wall = new Defence_Wall();
	private Depot depot = new Depot();
	private Academy academy = new Academy();

	public int[] getMaxDepotSpace() {
		return maxDepotSpace;
	}

	public void setMaxDepotSpace(int[] maxDepotSpace) {
		this.maxDepotSpace = maxDepotSpace;
	}

	public void setLumberjack(Lumberjack lumberjack) {
		this.lumberjack = lumberjack;
	}

	public void setFarm(Farm farm) {
		this.farm = farm;
	}

	public void setQuarry(Quarry quarry) {
		this.quarry = quarry;
	}

	public void setTimber_yard(Timber_Yard timber_yard) {
		this.timber_yard = timber_yard;
	}

	public void setBarrack(Barracks barrack) {
		this.barrack = barrack;
	}

	public void setManufacture(Siege_Manufacture manufacture) {
		this.manufacture = manufacture;
	}

	public void setFishing_hut(Fishing_Hut fishing_hut) {
		this.fishing_hut = fishing_hut;
	}

	public void setOre_mine(Ore_Mine ore_mine) {
		this.ore_mine = ore_mine;
	}

	public void setResidential_area(Residential_Area residential_area) {
		this.residential_area = residential_area;
	}

	public void setDefence_wall(Defence_Wall defence_wall) {
		this.defence_wall = defence_wall;
	}

	public void setDepot(Depot depot) {
		this.depot = depot;
	}

	public void setAcademy(Academy academy) {
		this.academy = academy;
	}

	// Buildpoints:
	private int buildProgressPerRound = 10;
	private int buildProgressPerRoundWithoutTimberYard = 10;

	public City(String name, Technology technology) {
		this.technology = technology;
		units = new UnitSet(technology);
		buildings = new ArrayList<AbstractBuilding>();
		this.setName(name);

		farm.builded();
		lumberjack.builded();
		quarry.builded();

		buildings.add(farm);
		buildings.add(fishing_hut);
		buildings.add(lumberjack);
		buildings.add(quarry);
		buildings.add(ore_mine);
		buildings.add(residential_area);
		buildings.add(defence_wall);
		buildings.add(timber_yard);
		buildings.add(depot);
		buildings.add(barrack);
		buildings.add(manufacture);
		buildings.add(academy);

		refreshUpdateWares();
	}

	public void refreshUpdateWares() {
		int[] newDiff = { 0, 0, 0, 0, 0 };
		for (int x = 0; x < buildings.size(); x++) {
			if (buildings.get(x).getLevel() != 0) {

				for (int i = 0; i < newDiff.length; i++) {
					int plus = 0;
					int minus = 0;
					if (buildings.get(x).getEarn() != null)
						plus = buildings.get(x).getEarn()
								.getWare(buildings.get(x).getLevel(), i);
					if (buildings.get(x).getCosts() != null)
						minus = buildings.get(x).getCosts()
								.getSupply(buildings.get(x).getLevel(), i);
					newDiff[i] = newDiff[i] + plus - minus;
				}

			}

		}
		for (int i = 0; i < newDiff.length; i++) {
			updateWares[i] = updateWares[i] + newDiff[i];
		}
	}

	public Academy getAcademy() {
		return academy;
	}

	public int[] getUpdateWares() {
		return updateWares;
	}

	public void setUpdateWares(int[] updateWares) {
		this.updateWares = updateWares;
	}

	public int getMaxDepotSpace(int index) {
		return maxDepotSpace[index];
	}

	public Lumberjack getLumberjack() {
		return lumberjack;
	}

	public Farm getFarm() {
		return farm;
	}

	public Quarry getQuarry() {
		return quarry;
	}

	public Defence_Wall getDefence_wall() {
		return defence_wall;
	}

	public Timber_Yard getTimber_yard() {
		return timber_yard;
	}

	public Fishing_Hut getFishing_hut() {
		return fishing_hut;
	}

	public Ore_Mine getOre_mine() {
		return ore_mine;
	}

	public Residential_Area getResidential_area() {
		return residential_area;
	}

	public Depot getDepot() {
		return depot;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public int getWares(int index) {
		return wares[index];
	}

	public int getBuildProgressPerRound() {
		if (timber_yard.isActive() && timber_yard.isBuilded()) {
			return buildProgressPerRound;
		} else {
			return buildProgressPerRoundWithoutTimberYard;
		}

	}

	public void setBuildProgressPerRound(int buildProgressPerRound) {
		this.buildProgressPerRound = buildProgressPerRound;
	}

	public void buildOrUpgradeBuilding(AbstractBuilding b)
			throws BuildingAlreadyExitsException, NotEnoughWaresException {
		if (b.getBuildCountdown() != 0) {
			// TODO: Anpassen. Diese Warnung erscheint nicht wenn das Gebaeude
			// existiert,
			// sondern wenn es grad am upgraden oder bauen ist.
			throw new BuildingAlreadyExitsException("");
		}
		if (!hasCityEnoughWaresToAffordBuilding(b)) {
			throw new NotEnoughWaresException("");
		}
		double sumCost = 0;
		int partCost;
		for (int i = 0; i < wares.length; i++) {
			partCost = b.getCosts().getWare(b.getLevel() + 1, i);
			wares[i] = wares[i] - partCost;
			sumCost = sumCost + partCost;
		}
		double ln = Math.log(b.getLevel() + 1);
		sumCost = sumCost * (1 + ln * ln) * 1.5;
		b.setBuildCountdown((int) sumCost);
		b.setLastFullBuildCountdown((int) sumCost);

	}

	public void increaseDepotSpace(int size) {
		for (int i = 0; i < wares.length; i++) {
			maxDepotSpace[i] = maxDepotSpace[i] + size;
		}
	}

	public boolean hasCityEnoughWaresToAffordBuilding(AbstractBuilding b) {
		for (int i = 0; i < wares.length; i++) {
			if (wares[i] - b.getCosts().getWare(b.getLevel() + 1, i) < 0) {
				return false;
			}
		}
		return true;
	}

	public boolean canAffordSupply(AbstractBuilding b) {
		for (int i = 0; i < wares.length; i++) {
			if (wares[i] - b.getCosts().getSupply(b.getLevel(), i) < 0) {
				return false;
			}
		}
		return true;
	}

	public void round() {
		for (int i = 0; i < updateWares.length; i++) {
			int result = wares[i] + updateWares[i];
			if (result >= 0) {
				if (result > maxDepotSpace[i]) {
					wares[i] = maxDepotSpace[i];
				} else {
					wares[i] = result;
				}

			} else {
				for (int j = 0; j < buildings.size(); j++) {
					if (buildings.get(j).isBuilded()
							&& buildings.get(j).isActive()) {
						Costs c = buildings.get(j).getCosts();
						if (c != null) {
							if (c.getSupply(buildings.get(j).getLevel(), i) > 0) {
								buildings.get(j).setActive(false, this);
							}
						}
					}
				}
			}
		}
		for (AbstractBuilding a : buildings) {

			/*
			 * if (a.isBuilded()) { a.checkEarning(this); a.paySupply(this); }
			 */

			if (a.getBuildCountdown() > 0) {
				int tmp = a.getBuildCountdown() - getBuildProgressPerRound();
				if (tmp <= 0) {
					a.setBuildCountdown(0);
					if (a.isActive()) {
						int[] oldDiff = { 0, 0, 0, 0, 0 };
						for (int i = 0; i < oldDiff.length; i++) {
							if (a.getLevel() != 0) {
								int plus = 0;
								int minus = 0;
								if (a.getEarn() != null)
									plus = a.getEarn().getWare(a.getLevel(), i);
								if (a.getCosts() != null)
									minus = a.getCosts().getSupply(
											a.getLevel(), i);
								oldDiff[i] = plus - minus;
							}
						}
						int[] newDiff = { 0, 0, 0, 0, 0 };
						for (int i = 0; i < newDiff.length; i++) {
							int plus = 0;
							int minus = 0;
							if (a.getEarn() != null)
								plus = a.getEarn().getWare(a.getLevel() + 1, i);
							if (a.getCosts() != null)
								minus = a.getCosts().getSupply(
										a.getLevel() + 1, i);

							newDiff[i] = plus - minus;
						}
						for (int i = 0; i < newDiff.length; i++) {
							updateWares[i] = updateWares[i]
									+ (newDiff[i] - oldDiff[i]);
						}
					}

					if (a.isBuilded()) {
						a.levelup();
					} else {
						a.builded();
					}

					// Sonderbehandlung fuer bestimmte Gebaeude:
					if (a instanceof Depot) {
						increaseDepotSpace(a.getLevel() * 100 + 400);
					} else if (a instanceof Timber_Yard) {
						double ln = Math.log(a.getLevel());
						setBuildProgressPerRound((int) (a.getLevel() * 10 * (ln
								* ln + 1))
								+ buildProgressPerRoundWithoutTimberYard);
					}
				} else {
					a.setBuildCountdown(tmp);
				}
			}
		}

		// BARRACKS:
		if (barrack.isActive()) {
			if (barrack.getUnitsToBuild().size() > 0) {
				int buildpoints = barrack.getLevel() * 10;
				while (buildpoints > 0 && barrack.getUnitsToBuild().size() > 0) {
					UnitOrder u = barrack.getUnitsToBuild().get(0);
					if (!u.isOrderComplete()) {
						int uPoints = u.getBuildCountdown();
						if (uPoints != 0) {
							if (uPoints > buildpoints) {
								u.setBuildCountdown(uPoints - buildpoints);
								buildpoints = 0;
							} else {
								buildpoints = buildpoints - uPoints;
								boolean orderComplete = u.orderProgress();
								if (orderComplete) {
									barrack.getUnitsToBuild().remove(0);
									u.setBuildCountdown(0);
								} else {
									u.setBuildCountdown(u
											.getFullBuildCountdown());
								}
								this.units.addUnit(u.getUnit());
							}

						}
					}

				}
			}
		}

		// MANUFACTUR
		if (manufacture.isActive()) {
			if (manufacture.getUnitsToBuild().size() > 0) {
				int buildpoints = manufacture.getLevel() * 10;
				while (buildpoints > 0
						&& manufacture.getUnitsToBuild().size() > 0) {
					UnitOrder u = manufacture.getUnitsToBuild().get(0);
					if (!u.isOrderComplete()) {
						int uPoints = u.getBuildCountdown();
						if (uPoints != 0) {
							if (uPoints > buildpoints) {
								u.setBuildCountdown(uPoints - buildpoints);
								buildpoints = 0;
							} else {
								buildpoints = buildpoints - uPoints;
								boolean orderComplete = u.orderProgress();
								if (orderComplete) {
									manufacture.getUnitsToBuild().remove(0);
									u.setBuildCountdown(0);
								} else {
									u.setBuildCountdown(u
											.getFullBuildCountdown());
								}
								this.units.addUnit(u.getUnit());
							}

						}
					}

				}
			}
		}
	}

	public String waresToString() {
		return wares[0] + " " + waresNames[0] + " " + wares[1] + " "
				+ waresNames[1] + " " + wares[2] + " " + waresNames[2] + "\n"
				+ wares[3] + " " + waresNames[3] + " " + wares[4] + " "
				+ waresNames[4] + ".";
	}

	public void earn(int index, int count) {
		wares[index] = wares[index] + count;
		if (wares[index] > maxDepotSpace[index]) {
			wares[index] = maxDepotSpace[index];
		}
	}

	public void pay(int index, int count) {
		wares[index] = wares[index] - count;
	}

	public UnitSet getUnits() {
		return units;
	}

	public ArrayList<AbstractBuilding> getBuildings() {
		return buildings;
	}

	public Barracks getBarrack() {
		return barrack;
	}

	public void buildUnits(UnitOrder u) throws NotEnoughWaresException {
		if (!hasCityEnoughWaresToAffordUnits(u)) {
			throw new NotEnoughWaresException("");
		}
		double sumCost = 0;
		int partCost;
		for (int i = 0; i < wares.length; i++) {
			partCost = (u.getUnit().getCosts().getWare(1, i))
					* u.getFullBuildCount();
			wares[i] = wares[i] - partCost;
			sumCost = sumCost + partCost;
		}
		if (u.getUnit().getBuildIn() == AbstractUnit.BUILD_IN_BARRACKS) {
			barrack.getUnitsToBuild().add(u);
		} else if (u.getUnit().getBuildIn() == AbstractUnit.BUILD_IN_MANUFRACTURE) {
			manufacture.getUnitsToBuild().add(u);
		}

	}

	public boolean hasCityEnoughWaresToAffordUnits(UnitOrder u) {
		for (int i = 0; i < wares.length; i++) {
			if (wares[i] - (u.getUnit().getCosts().getWare(1, i))
					* u.getFullBuildCount() < 0) {
				return false;
			}
		}
		return true;
	}

	public int howMuchUnitsCanWeAfford(AbstractUnit u) {
		int howMuch = 10000;
		int howMuchWares;
		for (int i = 0; i < wares.length; i++) {
			if (u.getCosts().getWare(1, i) != 0) {
				howMuchWares = wares[i] / u.getCosts().getWare(1, i);
			} else {
				howMuchWares = 10000;
			}
			if (howMuchWares < howMuch) {
				howMuch = howMuchWares;
			}
		}
		return howMuch;
	}

	public Siege_Manufacture getManufacture() {
		return manufacture;
	}

	public void setUnits(UnitSet units) {
		this.units = units;
	}

}
